<!DOCTYPE html>
<html>
<head><meta name="generator" content="Hexo 3.9.0">
    

    

    



    <meta charset="utf-8">
    
    
    
    <title>线程同步 | 奔跑的蜗牛 | 虚心若愚</title>
    <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
    
    <meta name="theme-color" content="#3F51B5">
    
    
    <meta name="keywords" content="线程同步">
    <meta name="description" content="线程安全问题非线程安全主要是指多个线程对一个对象中的同一个实例变量进行操作时会出现。 同步代码块为了解决线程安全问题，Java多线程引入了同步监视器来解决这个问题，同步监视器的通用方法就是同步代码块。定义如下： synchronized(obj) &amp;#123;    //此处的代码就是同步代码块&amp;#125;  obj就是同步监视器，上面代码的含义是：线程开始执行同步代码块之前，必须先获得对同步监视">
<meta name="keywords" content="线程同步">
<meta property="og:type" content="article">
<meta property="og:title" content="线程同步">
<meta property="og:url" content="http://windynature.oschina.io/2017/04/12/java/多线程/线程同步/index.html">
<meta property="og:site_name" content="奔跑的蜗牛">
<meta property="og:description" content="线程安全问题非线程安全主要是指多个线程对一个对象中的同一个实例变量进行操作时会出现。 同步代码块为了解决线程安全问题，Java多线程引入了同步监视器来解决这个问题，同步监视器的通用方法就是同步代码块。定义如下： synchronized(obj) &amp;#123;    //此处的代码就是同步代码块&amp;#125;  obj就是同步监视器，上面代码的含义是：线程开始执行同步代码块之前，必须先获得对同步监视">
<meta property="og:locale" content="zh-CN">
<meta property="og:updated_time" content="2020-03-18T04:10:04.257Z">
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="线程同步">
<meta name="twitter:description" content="线程安全问题非线程安全主要是指多个线程对一个对象中的同一个实例变量进行操作时会出现。 同步代码块为了解决线程安全问题，Java多线程引入了同步监视器来解决这个问题，同步监视器的通用方法就是同步代码块。定义如下： synchronized(obj) &amp;#123;    //此处的代码就是同步代码块&amp;#125;  obj就是同步监视器，上面代码的含义是：线程开始执行同步代码块之前，必须先获得对同步监视">
    
        <link rel="alternative" href="/atom.xml" title="奔跑的蜗牛" type="application/atom+xml">
    
    <link rel="shortcut icon" href="/blog/favicon.ico">
    <link rel="stylesheet" href="//unpkg.com/hexo-theme-material-indigo@1.5.2/css/style.css">
    <script>window.lazyScripts=[]</script>
</head>

<body>
    <div id="loading" class="active"></div>

    <aside id="menu" class="hide" >
  <div class="inner flex-row-vertical">
    <a href="javascript:;" class="header-icon waves-effect waves-circle waves-light" id="menu-off">
        <i class="icon icon-lg icon-close"></i>
    </a>
    <div class="brand-wrap" style="background-image:url(/blog/img/timg.jpg)">
      <div class="brand">
        <a href="/blog/" class="avatar waves-effect waves-circle waves-light">
          <img src="/blog/img/user2.jpg">
        </a>
        <hgroup class="introduce">
          <h5 class="nickname">windy</h5>
          <a href="mailto:windynature@163.com" title="windynature@163.com" class="mail">windynature@163.com</a>
        </hgroup>
      </div>
    </div>
    <div class="scroll-wrap flex-col">
      <ul class="nav">
        
            <li class="waves-block waves-effect">
              <a href="/blog/"  >
                <i class="icon icon-lg icon-home"></i>
                主页
              </a>
            </li>
        
            <li class="waves-block waves-effect">
              <a href="/blog/archives"  >
                <i class="icon icon-lg icon-archives"></i>
                归档
              </a>
            </li>
        
            <li class="waves-block waves-effect">
              <a href="/blog/tags"  >
                <i class="icon icon-lg icon-tags"></i>
                标签
              </a>
            </li>
        
            <li class="waves-block waves-effect">
              <a href="/blog/categories"  >
                <i class="icon icon-lg icon-th-list"></i>
                分类
              </a>
            </li>
        
            <li class="waves-block waves-effect">
              <a href="https://github.com/WindyNature" target="_blank" >
                <i class="icon icon-lg icon-github"></i>
                Github
              </a>
            </li>
        
            <li class="waves-block waves-effect">
              <a href="https://git.oschina.net/windynature" target="_blank" >
                <i class="icon icon-lg icon-mayun"></i>
                码云
              </a>
            </li>
        
      </ul>
    </div>
  </div>
</aside>

    <main id="main">
        <header class="top-header" id="header">
    <div class="flex-row">
        <a href="javascript:;" class="header-icon waves-effect waves-circle waves-light on" id="menu-toggle">
          <i class="icon icon-lg icon-navicon"></i>
        </a>
        <div class="flex-col header-title ellipsis">线程同步</div>
        
        <div class="search-wrap" id="search-wrap">
            <a href="javascript:;" class="header-icon waves-effect waves-circle waves-light" id="back">
                <i class="icon icon-lg icon-chevron-left"></i>
            </a>
            <input type="text" id="key" class="search-input" autocomplete="off" placeholder="输入感兴趣的关键字">
            <a href="javascript:;" class="header-icon waves-effect waves-circle waves-light" id="search">
                <i class="icon icon-lg icon-search"></i>
            </a>
        </div>
        
        
        <a href="javascript:;" class="header-icon waves-effect waves-circle waves-light" id="menuShare">
            <i class="icon icon-lg icon-share-alt"></i>
        </a>
        
    </div>
</header>
<header class="content-header post-header">

    <div class="container fade-scale">
        <h1 class="title">线程同步</h1>
        <h5 class="subtitle">
            
                <time datetime="2017-04-12T11:36:10.000Z" itemprop="datePublished" class="page-time">
  2017-04-12
</time>


	<ul class="article-category-list"><li class="article-category-list-item"><a class="article-category-list-link" href="/blog/categories/多线程/">多线程</a></li></ul>

            
        </h5>
    </div>

    

</header>


<div class="container body-wrap">
    
    <aside class="post-widget">
        <nav class="post-toc-wrap" id="post-toc">
            <h4>TOC</h4>
            <ol class="post-toc"><li class="post-toc-item post-toc-level-1"><a class="post-toc-link" href="#线程安全问题"><span class="post-toc-text">线程安全问题</span></a></li><li class="post-toc-item post-toc-level-1"><a class="post-toc-link" href="#同步代码块"><span class="post-toc-text">同步代码块</span></a></li><li class="post-toc-item post-toc-level-1"><a class="post-toc-link" href="#同步方法"><span class="post-toc-text">同步方法</span></a></li><li class="post-toc-item post-toc-level-1"><a class="post-toc-link" href="#释放同步监视器的锁定"><span class="post-toc-text">释放同步监视器的锁定</span></a></li><li class="post-toc-item post-toc-level-1"><a class="post-toc-link" href="#同步锁（Lock）"><span class="post-toc-text">同步锁（Lock）</span></a></li><li class="post-toc-item post-toc-level-1"><a class="post-toc-link" href="#死锁"><span class="post-toc-text">死锁</span></a></li></ol>
        </nav>
    </aside>
    
<article id="post-java/多线程/线程同步"
  class="post-article article-type-post fade" itemprop="blogPost">

    <div class="post-card">
        <h1 class="post-card-title">线程同步</h1>
        <div class="post-meta">
            <time class="post-time" title="2017年04月12日 19:36" datetime="2017-04-12T11:36:10.000Z"  itemprop="datePublished">2017-04-12</time>

            
	<ul class="article-category-list"><li class="article-category-list-item"><a class="article-category-list-link" href="/blog/categories/多线程/">多线程</a></li></ul>



            
<span id="busuanzi_container_page_pv" title="文章总阅读量" style='display:none'>
    <i class="icon icon-eye icon-pr"></i><span id="busuanzi_value_page_pv"></span>
</span>


            

        </div>
        <div class="post-content" id="post-content" itemprop="postContent">
            <h1 id="线程安全问题"><a href="#线程安全问题" class="headerlink" title="线程安全问题"></a>线程安全问题</h1><p><code>非线程安全</code>主要是指多个线程对一个对象中的<strong>同一个实例变量进行操作</strong>时会出现。</p>
<h1 id="同步代码块"><a href="#同步代码块" class="headerlink" title="同步代码块"></a>同步代码块</h1><p>为了解决线程安全问题，Java多线程引入了同步监视器来解决这个问题，同步监视器的通用方法就是同步代码块。定义如下：</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">synchronized</span>(obj) &#123;</span><br><span class="line">    <span class="comment">//此处的代码就是同步代码块</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><strong><code>obj</code></strong>就是同步监视器，上面代码的含义是：<strong>线程开始执行同步代码块之前，必须先获得对同步监视器的锁定。</strong></p>
<blockquote>
<p>任何时刻只能有一个线程可以获得对同步监视器的锁定，当同步代码块执行完成后，该线程会释放对该同步监视器的锁定。</p>
</blockquote>
<p><strong>同步监视器的目的：</strong>阻止两个线程对同一个共享资源进行并发访问，因此<strong>推荐使用可能被并发访问的共享资源充当同步监视器。</strong></p>
<p>如一个经典的问题—银行取钱问题。</p>
<p><strong>Account.java</strong></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">Account</span> </span>&#123;</span><br><span class="line">    <span class="keyword">private</span> String accountNo; <span class="comment">//编号</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">double</span> balance; <span class="comment">//余额</span></span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">Account</span><span class="params">(String accountNo,<span class="keyword">double</span> balance)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.accountNo = accountNo;</span><br><span class="line">        <span class="keyword">this</span>.balance = balance;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> String <span class="title">getAccountNo</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> accountNo;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setAccountNo</span><span class="params">(String accountNo)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.accountNo = accountNo;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">double</span> <span class="title">getBalance</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> balance;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">setBalance</span><span class="params">(<span class="keyword">double</span> balance)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">this</span>.balance = balance;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">hashCode</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">return</span> accountNo.hashCode();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">boolean</span> <span class="title">equals</span><span class="params">(Object obj)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">if</span>(<span class="keyword">this</span>==obj)</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">        <span class="keyword">if</span>(obj !=<span class="keyword">null</span> &amp;&amp; obj.getClass() == Account<span class="class">.<span class="keyword">class</span>) </span>&#123;</span><br><span class="line">            Account target = (Account)obj;</span><br><span class="line">            <span class="keyword">return</span> target.getAccountNo().equals(accountNo);</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">return</span> <span class="keyword">false</span>;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><strong>DrawThread.java</strong></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">DrawThread</span> <span class="keyword">extends</span> <span class="title">Thread</span> </span>&#123;</span><br><span class="line">    <span class="comment">//模拟账户用户</span></span><br><span class="line">    <span class="keyword">private</span> Account account;</span><br><span class="line">    <span class="comment">//取钱数</span></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">double</span> drawAmount;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="title">DrawThread</span><span class="params">(String name, Account account, <span class="keyword">double</span> drawAmount)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">super</span>(name);</span><br><span class="line">        <span class="keyword">this</span>.account = account;</span><br><span class="line">        <span class="keyword">this</span>.drawAmount = drawAmount;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="comment">/**</span></span><br><span class="line"><span class="comment">         * 使用account作为同步监视器，任何线程进入线面的同步代码块之前</span></span><br><span class="line"><span class="comment">         * 必须先获得对account账户的锁定---其他线程无法获得锁，也就服务修改它</span></span><br><span class="line"><span class="comment">         * 这种做法符合："加锁-&gt;修改-&gt;释放锁"的逻辑</span></span><br><span class="line"><span class="comment">         */</span></span><br><span class="line">       <span class="keyword">synchronized</span> (account) &#123;</span><br><span class="line">           <span class="keyword">if</span> (account.getBalance() &gt;= drawAmount) &#123;</span><br><span class="line">               System.out.println(getName() + <span class="string">"取钱成功！吐出钞票："</span> + drawAmount);</span><br><span class="line"></span><br><span class="line">               <span class="keyword">try</span> &#123;</span><br><span class="line">                   Thread.sleep(<span class="number">1</span>);</span><br><span class="line">               &#125; <span class="keyword">catch</span> (InterruptedException ex) &#123;</span><br><span class="line">                   ex.printStackTrace();</span><br><span class="line">               &#125;</span><br><span class="line"></span><br><span class="line">               account.setBalance(account.getBalance()-drawAmount);</span><br><span class="line">               System.out.println(<span class="string">"\t余额为："</span> + account.getBalance());</span><br><span class="line">           &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">               System.out.println(getName()+<span class="string">"取钱失败！余额不足！"</span>);</span><br><span class="line">           &#125;</span><br><span class="line">       &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><strong>DrawTest.java</strong></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">DrawTest</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        Account ac = <span class="keyword">new</span> Account(<span class="string">"1234567"</span>,<span class="number">1000</span>);</span><br><span class="line">        <span class="comment">//模拟两个线程对一个账户取钱</span></span><br><span class="line">        <span class="keyword">new</span> DrawThread(<span class="string">"甲"</span>,ac,<span class="number">800</span>).start();</span><br><span class="line">        <span class="keyword">new</span> DrawThread(<span class="string">"乙"</span>,ac,<span class="number">800</span>).start();</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><strong>通过这种方式就可以保证并发线程在任何一个时刻只有一个线程可以进入修改共享资源的代码区（临界区），所以<code>同一时刻最多只有一个线程</code>处于临界区内，从而保证了线程的安全性。</strong></p>
<h1 id="同步方法"><a href="#同步方法" class="headerlink" title="同步方法"></a>同步方法</h1><p><strong><code>同步方法</code></strong>是指用<code>synchronized</code>关键字来修饰某个方法。</p>
<p>对于<code>synchronized</code>修饰的实例方法（非static方法）而言，不需要指定同步监视器，同步方法的监视器是<code>this</code>，也就是调用该方法的对象。</p>
<p>修改Account.java</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">synchronized</span> <span class="keyword">void</span> <span class="title">draw</span><span class="params">(<span class="keyword">double</span> drawAmount)</span> </span>&#123;</span><br><span class="line">       <span class="keyword">if</span> (account.getBalance() &gt;= drawAmount) &#123;</span><br><span class="line">               System.out.println(getName() + <span class="string">"取钱成功！吐出钞票："</span> + drawAmount);</span><br><span class="line"></span><br><span class="line">               <span class="keyword">try</span> &#123;</span><br><span class="line">                   Thread.sleep(<span class="number">1</span>);</span><br><span class="line">               &#125; <span class="keyword">catch</span> (InterruptedException ex) &#123;</span><br><span class="line">                   ex.printStackTrace();</span><br><span class="line">               &#125;</span><br><span class="line"></span><br><span class="line">               account.setBalance(account.getBalance()-drawAmount);</span><br><span class="line">               System.out.println(<span class="string">"\t余额为："</span> + account.getBalance());</span><br><span class="line">           &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">               System.out.println(getName()+<span class="string">"取钱失败！余额不足！"</span>);</span><br><span class="line">           &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>修改DrawThread.java</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="comment">/**</span></span><br><span class="line"><span class="comment">     * 直接调用account对象的draw()方法来执行取钱操作</span></span><br><span class="line"><span class="comment">     * 同步方法的同步监视器是this,this代表调用draw()方法的对象</span></span><br><span class="line"><span class="comment">     * 也就是说，线程进入draw()方法之前，必须先对account对象加锁</span></span><br><span class="line"><span class="comment">    **/</span></span><br><span class="line">    account.draw(drawAmount);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p><strong>可变类的线程安全是以降低程序的运行效率作为代价的</strong>，为了减少线程安全带来的负面影响，可采用如下策略：</p>
<ul>
<li>只对那些会改变竞争资源的方法同步。</li>
<li>可变类应该提供单线程环境和多线程环境即线程不安全版本和线程安全版本。如jdk中的StringBuilder和StringBuffer</li>
</ul>
<h1 id="释放同步监视器的锁定"><a href="#释放同步监视器的锁定" class="headerlink" title="释放同步监视器的锁定"></a>释放同步监视器的锁定</h1><p><strong>释放同步监视器的锁定的几种情况：</strong></p>
<ul>
<li>当前线程的同步方法、同步代码块执行结束，当前线程即释放同步监视器</li>
<li>当前线程在同步代码块、同步方法中遇到break、return终止了该代码块、该方法的继续执行，当前线程会释放同步监视器</li>
<li>当前线程在同步代码块、同步方法中出现了未处理的Error或Exception，导致了该代码块、该方法异常结束，当前线程将会释放同步监视器。</li>
<li>当前线程执行同步代码块或同步方法时，程序执行了同步监视器对象的wait()方法，则当前线程暂停，并释放同步监视器。</li>
</ul>
<p><strong>以下几种方法不会释放同步监视器：</strong></p>
<ul>
<li>程序调用Thread.sleep()、Thread.yield()方法来暂停当前线程的执行，当前线程不会释放同步监视器。</li>
<li>程序调用线程的suspend()方法将该线程挂起，该线程不会释放同步监视器。程序应尽量避免使用suspend()和resume方法来控制线程。</li>
</ul>
<h1 id="同步锁（Lock）"><a href="#同步锁（Lock）" class="headerlink" title="同步锁（Lock）"></a>同步锁（Lock）</h1><p><code>Lock</code>提供了比<code>synchronized</code>方法和<code>synchronized</code>代码块更广泛的锁定操作，<code>Lock</code>允许实现更灵活的结构，可以具有差别很大的属性，并且支持多个相关的<code>Condition</code>对象。<br><code>Lock</code>是控制多个线程对共享资源进行访问的工具。通常，锁提供了对共享资源的独占访问，每次只能有一个线程对Lock对象加锁，线程开始访问共享资源前应先获得Lock对象。</p>
<p>某些锁可能允许对共享资源并发访问，如<code>ReadWriteLock</code>(读写锁)，Lock、ReadWriteLock是Java5提供的两个根接口，并为Lock提供了<code>ReentrantLock</code>（可重入锁）实现类，为<code>ReadWriteLock</code>提供了<code>ReentrantReadWriteLock</code>实现类。</p>
<p><strong>jdk中ReentrantLock的使用示例：</strong></p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">X</span> </span>&#123;</span><br><span class="line">  <span class="keyword">private</span> <span class="keyword">final</span> ReentrantLock lock = <span class="keyword">new</span> ReentrantLock();</span><br><span class="line">  <span class="comment">// ...</span></span><br><span class="line"></span><br><span class="line">  <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">m</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    lock.lock();  <span class="comment">// block until condition holds</span></span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">      <span class="comment">// ... method body</span></span><br><span class="line">    &#125; <span class="keyword">finally</span> &#123;</span><br><span class="line">      lock.unlock()</span><br><span class="line">    &#125;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>使用ReentrantLock对象来进行同步，加锁和释放锁出现在不同的作用范围内，通常见识使用finally块来确保在必要时释放锁。</p>
<blockquote>
<p>提示：<br>使用Lock与使用同步方法有点像是，只是使用Lock时显示使用Lock对象作为同步锁，而使用同步锁方法时系统隐式使用当前对象作为同步监视器，同样都符合”加锁-&gt;修改-&gt;释放锁”的操作模式，而且使用Lock对象时每个Lock对象对应一个Account对象，一样可以保证对于同一个Account对象，同一时刻只能有一个线程进入临界区。</p>
</blockquote>
<p>同步方法或同步块代码使用与资源相关的、隐式的同步监视器，并且强制要求加锁和释放锁要出现在一个代码块结构中，而且当获取了多个锁时，他们必须以相反的顺序释放，且必须在与所有锁被获取时相同的范围内释放所有锁。</p>
<p>ReentrantLock锁具有<strong>可重入性</strong>，也就是说，一个线程可以对已被加锁的ReentrantLock锁再次加锁，ReentrantLock对象会维持一个计数器来追踪lock()方法的嵌套调用，线程在每次调用lock()加锁后，必须显式调用unlock()来释放锁，<strong>所以一段被锁保护的代码可以调用另一个被相同锁保护的方法。</strong></p>
<h1 id="死锁"><a href="#死锁" class="headerlink" title="死锁"></a>死锁</h1><p>当两个线程相互等待对方释放同步监视器时就会发生死锁，Java虚拟机没有监测，也没有采取措施来处理死锁情况，所以多线程编程时应该采取措施避免死锁出现。一旦出现死锁，整个程序既不会发生任何异常，也<code>不会给出任何提示</code>，<code>只是所有线程处于阻塞状态</code>，无法继续。</p>
<figure class="highlight java"><table><tr><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span>  <span class="title">A</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">synchronized</span> <span class="keyword">void</span> <span class="title">foo</span><span class="params">(B b)</span> </span>&#123;</span><br><span class="line">        System.out.println(<span class="string">"当前线程名： "</span> + Thread.currentThread().getName()</span><br><span class="line">        + <span class="string">" 进入了A实例的foo方法"</span>);</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            Thread.sleep(<span class="number">200</span>);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (InterruptedException ex) &#123;</span><br><span class="line">            ex.printStackTrace();</span><br><span class="line">        &#125;</span><br><span class="line">        System.out.println(<span class="string">"当前线程名： "</span> + Thread.currentThread().getName()</span><br><span class="line">        + <span class="string">"企图调用B实例的last方法"</span>);</span><br><span class="line">        b.last();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">synchronized</span> <span class="keyword">void</span> <span class="title">last</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        System.out.println(<span class="string">"进入了A实例的last方法内部"</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">B</span> </span>&#123;</span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">synchronized</span> <span class="keyword">void</span> <span class="title">bar</span><span class="params">(A a)</span> </span>&#123;</span><br><span class="line">        System.out.println(<span class="string">"当前线程名： "</span> + Thread.currentThread().getName()</span><br><span class="line">        + <span class="string">" 进入了B实例的bar方法"</span>);</span><br><span class="line">        <span class="keyword">try</span>&#123;</span><br><span class="line">            Thread.sleep(<span class="number">200</span>);</span><br><span class="line">        &#125; <span class="keyword">catch</span> (InterruptedException ex) &#123;</span><br><span class="line">            ex.printStackTrace();</span><br><span class="line">        &#125;</span><br><span class="line">        System.out.println(<span class="string">"当前线程名： "</span> + Thread.currentThread().getName()</span><br><span class="line">        + <span class="string">" 企图调用A实例的last方法"</span>);</span><br><span class="line">        a.last();</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">synchronized</span> <span class="keyword">void</span> <span class="title">last</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        System.out.println(<span class="string">"进入了B实例的last方法内部"</span>);</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">DeadLock</span> <span class="keyword">implements</span> <span class="title">Runnable</span>  </span>&#123;</span><br><span class="line"></span><br><span class="line">    A a = <span class="keyword">new</span> A();</span><br><span class="line">    B b = <span class="keyword">new</span> B();</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">init</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        Thread.currentThread().setName(<span class="string">"主线程"</span>);</span><br><span class="line">        a.foo(b);</span><br><span class="line">        System.out.println(<span class="string">"进入了主线程后"</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">run</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        Thread.currentThread().setName(<span class="string">"副线程"</span>);</span><br><span class="line">        <span class="comment">//调用b对象的bar()方法</span></span><br><span class="line">        b.bar(a);</span><br><span class="line">        System.out.println(<span class="string">"进入了副线程之后"</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">        DeadLock d1 = <span class="keyword">new</span> DeadLock();</span><br><span class="line">        <span class="comment">//以di为target启动新线程</span></span><br><span class="line">        <span class="keyword">new</span> Thread(d1).start();</span><br><span class="line">        d1.init();</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>上面的代码有两个线程</p>
<ol>
<li>主线程调用init()方法，调用A对象的foo()方法，A被锁住，且主线程暂停200ms,</li>
<li>这时CPU切换到副线程，副线程调用B的bar()方法，B被锁住，副线程暂停200ms；</li>
<li>主线程醒来想调动B的last()方法，但这是B被锁住所以主线程阻塞；</li>
<li>副线程醒来想调用A的last()方法，但主线程阻塞没有释放A的锁。</li>
<li>这时主线程保持着A对象的锁，等待对B对象加锁，而副线程保持着B对象的锁，等待对A对象加锁就造成了死锁。</li>
</ol>

        </div>

        <blockquote class="post-copyright">
    <div class="content">
        
<span class="post-time">
    最后更新时间：<time datetime="2020-03-18T04:10:04.257Z" itemprop="dateUpdated">2020年3月18日 12:10</time>
</span><br>


        这里写留言或版权声明：<a href="/blog/2017/04/12/java/多线程/线程同步/" target="_blank" rel="external">http://windynature.oschina.io/2017/04/12/java/多线程/线程同步/</a>
    </div>
    <footer>
        <a href="http://windynature.oschina.io">
            <img src="/blog/img/user2.jpg" alt="windy">
            windy
        </a>
    </footer>
</blockquote>

        


        <div class="post-footer">
            
	<ul class="article-tag-list"><li class="article-tag-list-item"><a class="article-tag-list-link" href="/blog/tags/线程同步/">线程同步</a></li></ul>


            
<div class="page-share-wrap">
    

<div class="page-share" id="pageShare">
    <ul class="reset share-icons">
      <li>
        <a class="weibo share-sns" target="_blank" href="http://service.weibo.com/share/share.php?url=http://windynature.oschina.io/2017/04/12/java/多线程/线程同步/&title=《线程同步》 — 奔跑的蜗牛&pic=http://windynature.oschina.io/img/user2.jpg" data-title="微博">
          <i class="icon icon-weibo"></i>
        </a>
      </li>
      <li>
        <a class="weixin share-sns wxFab" href="javascript:;" data-title="微信">
          <i class="icon icon-weixin"></i>
        </a>
      </li>
      <li>
        <a class="qq share-sns" target="_blank" href="http://connect.qq.com/widget/shareqq/index.html?url=http://windynature.oschina.io/2017/04/12/java/多线程/线程同步/&title=《线程同步》 — 奔跑的蜗牛&source=" data-title=" QQ">
          <i class="icon icon-qq"></i>
        </a>
      </li>
      <li>
        <a class="facebook share-sns" target="_blank" href="https://www.facebook.com/sharer/sharer.php?u=http://windynature.oschina.io/2017/04/12/java/多线程/线程同步/" data-title=" Facebook">
          <i class="icon icon-facebook"></i>
        </a>
      </li>
      <li>
        <a class="twitter share-sns" target="_blank" href="https://twitter.com/intent/tweet?text=《线程同步》 — 奔跑的蜗牛&url=http://windynature.oschina.io/2017/04/12/java/多线程/线程同步/&via=http://windynature.oschina.io" data-title=" Twitter">
          <i class="icon icon-twitter"></i>
        </a>
      </li>
      <li>
        <a class="google share-sns" target="_blank" href="https://plus.google.com/share?url=http://windynature.oschina.io/2017/04/12/java/多线程/线程同步/" data-title=" Google+">
          <i class="icon icon-google-plus"></i>
        </a>
      </li>
    </ul>
 </div>



    <a href="javascript:;" id="shareFab" class="page-share-fab waves-effect waves-circle">
        <i class="icon icon-share-alt icon-lg"></i>
    </a>
</div>



        </div>
    </div>

    
<nav class="post-nav flex-row flex-justify-between">
  
    <div class="waves-block waves-effect prev">
      <a href="/blog/2020/03/17/gis基本原理/比例尺及分辨率换算规则/" id="post-prev" class="post-nav-link">
        <div class="tips"><i class="icon icon-angle-left icon-lg icon-pr"></i> Prev</div>
        <h4 class="title">公网地图最大比例尺</h4>
      </a>
    </div>
  

  
    <div class="waves-block waves-effect next">
      <a href="/blog/2017/04/12/java/多线程/控制线程/" id="post-next" class="post-nav-link">
        <div class="tips">Next <i class="icon icon-angle-right icon-lg icon-pl"></i></div>
        <h4 class="title">控制线程</h4>
      </a>
    </div>
  
</nav>



    

<div class="comments" id="comments">
    <div class="ds-thread" data-thread-key="java/多线程/线程同步" data-title="线程同步" data-url="http://windynature.oschina.io/2017/04/12/java/多线程/线程同步/"></div>
</div>
<script>
lazyScripts.push('//cdn.bootcss.com/marked/0.3.6/marked.min.js');

var duoshuoQuery = {short_name:'ysblog', theme: 'none'};
lazyScripts.push('//unpkg.com/hexo-theme-material-indigo@1.5.2/js/embed.min.js');


</script>










</article>



</div>

        <footer class="footer">
    <div class="top">
        
<p>
    <span id="busuanzi_container_site_uv" style='display:none'>
        站点总访客数：<span id="busuanzi_value_site_uv"></span>
    </span>
    <span id="busuanzi_container_site_pv" style='display:none'>
        站点总访问量：<span id="busuanzi_value_site_pv"></span>
    </span>
</p>


        <p>
            <span><a href="/atom.xml" target="_blank" class="rss" title="rss"><i class="icon icon-lg icon-rss"></i></a></span>
            <span>博客内容遵循 <a href="http://creativecommons.org/licenses/by-nc-sa/4.0/" target="_blank">知识共享 署名 - 非商业性 - 相同方式共享 4.0协议</a></span>
        </p>
    </div>
    <div class="bottom">
        <p>
            <span>Power by <a href="http://hexo.io/" target="_blank">Hexo</a> Theme <a href="https://github.com/yscoder/hexo-theme-indigo" target="_blank">indigo</a></span>
            <span>奔跑的蜗牛 &copy; 2015 - 2020</span>
        </p>
    </div>
</footer>

    </main>
    <div class="mask" id="mask"></div>
<a href="javascript:;" id="gotop" class="waves-effect waves-circle waves-light"><span class="icon icon-lg icon-chevron-up"></span></a>



<div class="global-share" id="globalShare">
    <ul class="reset share-icons">
      <li>
        <a class="weibo share-sns" target="_blank" href="http://service.weibo.com/share/share.php?url=http://windynature.oschina.io/2017/04/12/java/多线程/线程同步/&title=《线程同步》 — 奔跑的蜗牛&pic=http://windynature.oschina.io/img/user2.jpg" data-title="微博">
          <i class="icon icon-weibo"></i>
        </a>
      </li>
      <li>
        <a class="weixin share-sns wxFab" href="javascript:;" data-title="微信">
          <i class="icon icon-weixin"></i>
        </a>
      </li>
      <li>
        <a class="qq share-sns" target="_blank" href="http://connect.qq.com/widget/shareqq/index.html?url=http://windynature.oschina.io/2017/04/12/java/多线程/线程同步/&title=《线程同步》 — 奔跑的蜗牛&source=" data-title=" QQ">
          <i class="icon icon-qq"></i>
        </a>
      </li>
      <li>
        <a class="facebook share-sns" target="_blank" href="https://www.facebook.com/sharer/sharer.php?u=http://windynature.oschina.io/2017/04/12/java/多线程/线程同步/" data-title=" Facebook">
          <i class="icon icon-facebook"></i>
        </a>
      </li>
      <li>
        <a class="twitter share-sns" target="_blank" href="https://twitter.com/intent/tweet?text=《线程同步》 — 奔跑的蜗牛&url=http://windynature.oschina.io/2017/04/12/java/多线程/线程同步/&via=http://windynature.oschina.io" data-title=" Twitter">
          <i class="icon icon-twitter"></i>
        </a>
      </li>
      <li>
        <a class="google share-sns" target="_blank" href="https://plus.google.com/share?url=http://windynature.oschina.io/2017/04/12/java/多线程/线程同步/" data-title=" Google+">
          <i class="icon icon-google-plus"></i>
        </a>
      </li>
    </ul>
 </div>


<div class="page-modal wx-share" id="wxShare">
    <a class="close" href="javascript:;"><i class="icon icon-close"></i></a>
    <p>扫一扫，分享到微信</p>
    <img src="" alt="微信分享二维码">
</div>




    <script src="//cdn.bootcss.com/node-waves/0.7.4/waves.min.js"></script>
<script>
var BLOG = { ROOT: '/blog/', SHARE: true, REWARD: false };



</script>

<script src="//unpkg.com/hexo-theme-material-indigo@1.5.2/js/main.min.js"></script>


<div class="search-panel" id="search-panel">
    <ul class="search-result" id="search-result"></ul>
</div>
<template id="search-tpl">
<li class="item">
    <a href="{path}" class="waves-block waves-effect">
        <div class="title ellipsis" title="{title}">{title}</div>
        <div class="flex-row flex-middle">
            <div class="tags ellipsis">
                {tags}
            </div>
            <time class="flex-col time">{date}</time>
        </div>
    </a>
</li>
</template>

<script src="//unpkg.com/hexo-theme-material-indigo@1.5.2/js/search.min.js" async></script>






<script async src="//dn-lbstatics.qbox.me/busuanzi/2.3/busuanzi.pure.mini.js"></script>





</body>
</html>
